package org.lonetree.tc.core;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.hibernate.Query;
import org.hibernate.Session;
import org.lonetree.tc.core.Book.Isbn;
import org.lonetree.tc.core.BookComment.BookCommentId;
import org.lonetree.tc.core.Course.CourseId;
import org.lonetree.tc.core.CourseComment.CourseCommentId;
import org.lonetree.tc.core.ElectronicSource.Url;
import org.lonetree.tc.core.Section.SectionId;
import org.lonetree.tc.core.Source.SourceId;
import org.lonetree.tc.core.User.EmailAddress;
import org.lonetree.tc.core.User.Username;
import org.lonetree.tc.core.dao.BookDAO;
import org.lonetree.tc.core.dao.CourseDAO;
import org.lonetree.tc.core.dao.UserDAO;
import org.lonetree.tc.core.exception.AttributeCannotBeNullException;
import org.lonetree.tc.core.exception.ItemAlreadyExistsException;
import org.lonetree.tc.core.exception.NoSuchItemException;
import org.lonetree.tc.core.exception.SearchException;
import org.lonetree.tc.core.id.ValidatedIdentifier.ValidationException;
import org.lonetree.tc.core.search.BookCriteria;
import org.lonetree.tc.core.search.CourseCriteria;
import org.lonetree.tc.core.search.UserCriteria;
import org.lonetree.tc.dao.hibernate.HibernateAction;
import org.lonetree.tc.dao.hibernate.HibernateHelper;
import org.lonetree.tc.dao.hibernate.dto.CourseCommentDTO;
import org.lonetree.tc.dao.hibernate.dto.CourseDTO;
import org.lonetree.tc.dao.hibernate.dto.SectionDTO;
import org.lonetree.tc.dao.hibernate.dto.SourceDTO;
import org.lonetree.tc.dao.hibernate.dto.TextbookCommentDTO;
import org.lonetree.tc.dao.hibernate.dto.TextbookDTO;
import org.lonetree.tc.dao.hibernate.dto.UserDTO;


public class DBDAOImpl implements UserDAO, BookDAO, CourseDAO{
	
	private UserDTO getUserDTO(Username username){
		Query q = HibernateHelper.getSession().createQuery("from UserDTO u where u.username = :uid");
		q.setParameter("uid", username.getUsernameString());
		return (UserDTO)q.uniqueResult();
	}
	
	public User getUser(Username username) throws NoSuchItemException{
		UserDTO u = getUserDTO(username);
		
		if(u == null)
			throw new NoSuchItemException();
		else
			return convertUserDTO(u, null, null, null);
	}

	public User createUser(Username username) throws ItemAlreadyExistsException{
		UserDTO u = getUserDTO(username);
		
		if(u != null){
			throw new ItemAlreadyExistsException();
		}
		else{
			final Username uname = username;
			HibernateAction action = new HibernateAction(){
				public Object execute(Session session){
					UserDTO u = new UserDTO();
					u.setUsername(uname.getUsernameString());
					session.save(u);
					return new User(uname);
				}
			};
			
			return (User)HibernateHelper.executeHibernateAction(action);
		}
	}

	public void removeUser(Username username) throws NoSuchItemException{
		final UserDTO u = getUserDTO(username);
		
		if(u == null){
			throw new NoSuchItemException();
		}
		else{
			HibernateAction action = new HibernateAction(){
				public Object execute(Session session){
					session.delete(u);
					return NOTHING;
				}
			};
			
			HibernateHelper.executeHibernateAction(action);
		}
	}

	public void updateUser(User user) throws NoSuchItemException, AttributeCannotBeNullException{
		final User u = user;
		final UserDTO udto = getUserDTO(u.getUsername());
		if(udto == null){
			throw new NoSuchItemException();
		}
		else{
			HibernateAction action = new HibernateAction(){
				@SuppressWarnings("unchecked")
				public Object execute(Session session){
					updateUserDTO(udto, u);
					return NOTHING;
				}
			};
			
			HibernateHelper.executeHibernateAction(action);
		}
	}
	
	@SuppressWarnings("unchecked")
	public Set<User> findUsers(UserCriteria criteria) throws SearchException{
		if(criteria == null){
			Query q = HibernateHelper.getSession().createQuery("from UserDTO");
			List<UserDTO> udtos = (List<UserDTO>)q.list();
			return convertUserDTOs(udtos, null, null, null, null);
		}
		else{
			// COMPLETE DBDAOImpl.findUsers()
			throw new RuntimeException("searching on specific user attributes is not implemented at this time");
		}
	}
	
	private TextbookDTO getTextbookDTO(Isbn isbn){
		Query q = HibernateHelper.getSession().createQuery("from TextbookDTO t where t.isbn13 = :isbn");
		q.setParameter("isbn", isbn.getIsbnString());
		return (TextbookDTO)q.uniqueResult();
	}
	
	public Book getBook(Isbn isbn) throws NoSuchItemException{
		TextbookDTO t = getTextbookDTO(isbn);
		
		if(t == null)
			throw new NoSuchItemException();
		else
			return convertTextbookDTO(t, null, null, null);
	}
	
	public Book createBook(Isbn isbn) throws ItemAlreadyExistsException{
		TextbookDTO t = getTextbookDTO(isbn);
		
		if(t != null){
			throw new ItemAlreadyExistsException();
		}
		else{
			final Isbn i = isbn;
			HibernateAction action = new HibernateAction(){
				public Object execute(Session session){
					TextbookDTO t = new TextbookDTO();
					t.setIsbn13(i.getIsbnString());
					session.save(t);
					return new Book(i);
				}
			};
			
			return (Book)HibernateHelper.executeHibernateAction(action);
		}
	}

	public void removeBook(Isbn isbn) throws NoSuchItemException{
		final TextbookDTO t = getTextbookDTO(isbn);
		
		if(t == null){
			throw new NoSuchItemException();
		}
		else{
			HibernateAction action = new HibernateAction(){
				public Object execute(Session session){
					session.delete(t);
					return NOTHING;
				}
			};
			
			HibernateHelper.executeHibernateAction(action);
		}
	}

	public void updateBook(Book book) throws NoSuchItemException, AttributeCannotBeNullException{
		final Book b = book;
		final TextbookDTO tdto = getTextbookDTO(b.getIsbn());
		if(tdto == null){
			throw new NoSuchItemException();
		}
		else{
			HibernateAction action = new HibernateAction(){
				@SuppressWarnings("unchecked")
				public Object execute(Session session){
					updateTextbookDTO(tdto, b);
					return NOTHING;
				}
			};
			
			HibernateHelper.executeHibernateAction(action);
		}
	}
	
	@SuppressWarnings("unchecked")
	public Set<Book> findBooks(BookCriteria criteria) throws SearchException{
		List<TextbookDTO> tdtos;
		Query q;
		
		if(criteria == null){
			q = HibernateHelper.getSession().createQuery("from TextbookDTO");
		}
		else{
			
			Isbn i = criteria.getIsbn();
			String i2;
			if(i == null)
				i2 = null;
			else
				i2 = i.getIsbnString();
			
			final String isbn = i2;
			final String author = criteria.getAuthor();
			final String title = criteria.getTitle();
			
			if(author == null && isbn == null && title == null)
				throw new SearchException("at least one criterion must be non-null");
			
			boolean firstClause = true;
			StringBuffer s = new StringBuffer();
			s.append("from TextbookDTO t ");
			if(author != null){
				s.append("where t.authors like :auth ");
				firstClause = false;
			}
			if(isbn != null){
				if(firstClause)
					s.append("where ");
				else
					s.append("and ");
				
				s.append("t.isbn13 = :isbn ");
				firstClause = false;
			}
			if(title != null){
				if(firstClause)
					s.append("where ");
				else
					s.append("and ");
				
				s.append("t.title like :title");
			}
			
			q = HibernateHelper.getSession().createQuery(s.toString());
			if(author != null)
				q.setParameter("auth", "%" + author + "%");
			if(isbn != null)
				q.setParameter("isbn", isbn);
			if(title != null)
				q.setParameter("title", "%" + title + "%");
		}
		
		tdtos = (List<TextbookDTO>)q.list();
		return convertTextbookDTOs(tdtos, null, null, null, null);
	}
	
	@SuppressWarnings("unchecked")
	public Set<BookComment> getFlaggedBookComments(){
		List<TextbookCommentDTO> bcdtos = (List<TextbookCommentDTO>)HibernateHelper.getSession().createQuery("from TextbookCommentDTO c where c.flagged = true").list();
		return convertTextbookCommentDTOs(bcdtos, null, null, null, null);
	}

	private CourseDTO getCourseDTO(CourseId courseId){
		String cidstr = courseId.getCourseIdString();
		Query q = HibernateHelper.getSession().createQuery("from CourseDTO c where c.courseCode = :cc and c.courseNumber = :cn");
		q.setParameter("cc", cidstr.substring(0, 4));
		q.setParameter("cn", cidstr.substring(4));
		return (CourseDTO)q.uniqueResult();
	}
	
	public Course getCourse(CourseId courseId) throws NoSuchItemException{
		CourseDTO c = getCourseDTO(courseId);
		
		if(c == null)
			throw new NoSuchItemException();
		else
			return convertCourseDTO(c, null, null, null);
	}

	public Course createCourse(CourseId courseId) throws ItemAlreadyExistsException{
		CourseDTO c = getCourseDTO(courseId);
		
		if(c != null){
			throw new ItemAlreadyExistsException();
		}
		else{
			final CourseId cid = courseId;
			HibernateAction action = new HibernateAction(){
				public Object execute(Session session){
					CourseDTO c = new CourseDTO();
					c.setCourseCode(cid.getDepartment());
					c.setCourseNumber(cid.getNumber());
					session.save(c);
					return new Course(cid);
				}
			};
			
			return (Course)HibernateHelper.executeHibernateAction(action);
		}
	}

	public void removeCourse(CourseId courseId) throws NoSuchItemException{
		final CourseDTO c = getCourseDTO(courseId);
		
		if(c == null){
			throw new NoSuchItemException();
		}
		else{
			HibernateAction action = new HibernateAction(){
				public Object execute(Session session){
					session.delete(c);
					return NOTHING;
				}
			};
			
			HibernateHelper.executeHibernateAction(action);
		}
	}

	public void updateCourse(Course course) throws NoSuchItemException, AttributeCannotBeNullException{
		final Course c = course;
		final CourseDTO cdto = getCourseDTO(c.getCourseId());
		if(cdto == null){
			throw new NoSuchItemException();
		}
		else{
			HibernateAction action = new HibernateAction(){
				@SuppressWarnings("unchecked")
				public Object execute(Session session){
					updateCourseDTO(cdto, c);
					return NOTHING;
				}
			};
			
			HibernateHelper.executeHibernateAction(action);
		}
	}
	
	@SuppressWarnings("unchecked")
	public Set<Course> findCourses(CourseCriteria criteria) throws SearchException{
		List<CourseDTO> cdtos;
		Query q;
		
		if(criteria == null){
			q = HibernateHelper.getSession().createQuery("from CourseDTO");
		}
		else{
			final String courseNumber = criteria.getCourseNumber();
			final String courseDept = criteria.getCourseDept();
			final String title = criteria.getTitle();
			
			if(courseNumber == null && courseDept == null && title == null)
				throw new SearchException("at least one criterion must be non-null");
			
			boolean firstClause = true;
			StringBuffer s = new StringBuffer();
			s.append("from CourseDTO c ");
			if(courseNumber != null){
				s.append("where c.courseNumber :cn ");
				firstClause = false;
			}
			if(courseDept != null){
				if(firstClause)
					s.append("where ");
				else
					s.append("and ");
				
				s.append("c.courseCode = :cc ");
				firstClause = false;
			}
			if(title != null){
				if(firstClause)
					s.append("where ");
				else
					s.append("and ");
				
				s.append("c.courseTitle like :title");
			}
			
			q = HibernateHelper.getSession().createQuery(s.toString());
			if(courseNumber != null)
				q.setParameter("cn", courseNumber);
			if(courseDept != null)
				q.setParameter("cc", courseDept);
			if(title != null)
				q.setParameter("title",  "%" + title + "%");
		}
		
		cdtos = (List<CourseDTO>)q.list();
		return convertCourseDTOs(cdtos, null, null, null);
	}
	
	@SuppressWarnings("unchecked")
	public Set<CourseComment> getFlaggedCourseComments(){
		List<CourseCommentDTO> ccdtos = (List<CourseCommentDTO>)HibernateHelper.getSession().createQuery("from CourseCommentDTO c where c.flagged = true").list();
		return convertCourseCommentDTOs(ccdtos, null, null, null, null);
	}

	private Set<TextbookDTO> convertBooksToDTOs(Collection<Book> books){
		if(books == null)
			return new HashSet<TextbookDTO>();
		
		Set<TextbookDTO> b = new HashSet<TextbookDTO>(books.size());
		for(Iterator<Book> it = books.iterator(); it.hasNext();){
			b.add(convertBookToDTO(it.next()));
		}
		return b;
	}
	
	private TextbookDTO convertBookToDTO(Book book){
		if(book == null)
			return null;
		
		TextbookDTO tdto = getTextbookDTO(book.getIsbn());
		updateTextbookDTO(tdto, book);
		return tdto;
	}
	
	private UserDTO convertUserToDTO(User user){
		if(user == null)
			return null;
		
		UserDTO udto = getUserDTO(user.getUsername());
		updateUserDTO(udto, user);
		return udto;
	}
	
	private void updateTextbookDTO(TextbookDTO tdto, Book b) throws AttributeCannotBeNullException{
		tdto.setAuthors(delimitCollection(b.getAuthors(), "$"));
		tdto.setComments(convertBookCommentsToDTOs(b.getComments(), tdto, null));
		tdto.setOptional(b.isOptional());
		tdto.setSections(convertSectionsToDTOs(b.getSections()));
		tdto.setSources(convertSourcesToDTOs(b.getSources()));
		tdto.setTitle(b.getTitle());
	}
	
	private String delimitCollection(Collection<String> c, String delimiter){
		StringBuffer strbuf = new StringBuffer();
		for(Iterator<String> it = c.iterator(); it.hasNext();){
			strbuf.append(it.next());
			if(it.hasNext())
				strbuf.append(delimiter);
		}
		return strbuf.toString();
	}
	
	private void updateUserDTO(UserDTO udto, User u) throws AttributeCannotBeNullException{
		udto.setEmail(((EmailAddress)checkForNullAttr(u.getEmailAddress(), "email address")).getEmailAddressString());
		udto.setFirstName((String)checkForNullAttr(u.getFirstName(), "first name"));
		udto.setLastName((String)checkForNullAttr(u.getLastName(), "last name"));
		udto.setLocation(u.getLocation());
		udto.setTextbooks(convertBooksToDTOs(u.getMyBooks()));
		udto.setSections(convertSectionsToDTOs(u.getMySections()));
		udto.setPwd((String)checkForNullAttr(u.getPassword(), "password"));
		udto.setSuspended(u.isSuspended());
		udto.setRole(u.isAdmin() ? 1 : 0);
	}
	
	private void updateCourseDTO(CourseDTO cdto, Course c) throws AttributeCannotBeNullException{
		cdto.setCourseComments(convertCourseCommentsToDTOs(c.getComments()));
		cdto.setCourseTitle(c.getTitle());
		cdto.setSections(convertSectionsToDTOs(c.getSections()));
	}
	
	private Set<Book> convertTextbookDTOs(Collection<TextbookDTO> tdtos, Section s, Course c, Book b, User u){
		if(tdtos == null)
			return null;
		
		Set<Book> bs = new HashSet<Book>(tdtos.size());
		for(Iterator<TextbookDTO> it = tdtos.iterator(); it.hasNext();){
			TextbookDTO tdto = it.next();
			try{
				if(b != null && b.getIsbn().equals(new Isbn(tdto.getIsbn13())))
					bs.add(b);
				else
					bs.add(convertTextbookDTO(tdto, s, c, u));
			}
			catch(ValidationException e){
				throw new RuntimeException(e);
			}
		}
		return bs;
	}

	private Book convertTextbookDTO(TextbookDTO tdto, Section s, Course c, User u){
		if(tdto == null)
			return null;
		
		try{
			Book b = new Book(
					new Isbn(tdto.getIsbn13()),
					tdto.getTitle(),
					convertAuthorString(tdto.getAuthors()),
					null,
					null,
					null,
					tdto.isOptional()
				);
			
			b.getComments().addAll(convertTextbookCommentDTOs(tdto.getComments(), s, c, b, u));
//			b.getSections().addAll(convertSectionDTOs(tdto.getSections(), s, c, b, u)); // REVERT
            b.getSources().addAll(convertSourceDTOs(tdto.getSources(), s, c, b, u)); // VERIFY this works
			
			return b;
		}
		catch(ValidationException e){
			throw new RuntimeException(e);
		}
	}
	
	private Set<String> convertAuthorString(String s){
		if(s == null)
			return null;
		
		String[] sa = s.split("\\$");
		Set<String> set = new HashSet<String>();
		for(int i = 0; i<sa.length; i++){
			set.add(sa[i]);
		}
		return set;
	}
	
	private Set<Source> convertSourceDTOs(Collection<SourceDTO> sdtos, Section s, Course c, Book b, User u){
		if(sdtos == null)
			return new HashSet<Source>();
		
		HashSet<Source> ss = new HashSet<Source>(sdtos.size());
		for(Iterator<SourceDTO> it = sdtos.iterator(); it.hasNext();){
			ss.add(convertSourceDTO(it.next(), s, c, b, u));
		}
		return ss;
	}
	
	private Source convertSourceDTO(SourceDTO sdto, Section s, Course c, Book b, User u){
		if(b == null)
			b = convertTextbookDTO(sdto.getTextbook(), s, c, u);
		
		try{
			int type = sdto.getSourceType();
			switch(type){
				case SourceDTO.SOURCE_TYPE_ELECTRONIC:
					return new ElectronicSource(new SourceId((long)sdto.getId()), b, sdto.getName(), sdto.getPrice(), sdto.getSourceDetails(), new Url(sdto.getSourceAddress()));
				case SourceDTO.SOURCE_TYPE_PHYSICAL:
					return new PhysicalSource(new SourceId((long)sdto.getId()), b, sdto.getName(), sdto.getPrice(), sdto.getSourceDetails(), sdto.getSourceAddress());
				case SourceDTO.SOURCE_TYPE_USER:
					if(u == null)
						u = convertUserDTO(sdto.getUser(), s, c, b);
					return new UserSource(new SourceId((long)sdto.getId()), b, sdto.getPrice(), sdto.getSourceDetails(), u);
				default:
					throw new RuntimeException("source type " + type + " is not a known type");
			}
		}
		catch(ValidationException e){
			throw new RuntimeException(e);
		}
	}
	
	private Set<BookComment> convertTextbookCommentDTOs(Collection<TextbookCommentDTO> tcdtos, Section s, Course c, Book b, User u){
		if(tcdtos == null)
			return new HashSet<BookComment>();
		
		HashSet<BookComment> bc = new HashSet<BookComment>(tcdtos.size());
		for(Iterator<TextbookCommentDTO> it = tcdtos.iterator(); it.hasNext();){
			bc.add(convertTextbookCommentDTO(it.next(), s, c, b, u));
		}
		return bc;
	}
	
	private Set<CourseComment> convertCourseCommentDTOs(Collection<CourseCommentDTO> ccdtos, Section s, Course c, Book b, User u){
		if(ccdtos == null)
			return new HashSet<CourseComment>();
		
		HashSet<CourseComment> cc = new HashSet<CourseComment>(ccdtos.size());
		for(Iterator<CourseCommentDTO> it = ccdtos.iterator(); it.hasNext();){
			cc.add(convertCourseCommentDTO(it.next(), s, c, b, u));
		}
		return cc;
	}
	
	private CourseComment convertCourseCommentDTO(CourseCommentDTO ccdto, Section s, Course c, Book b, User u){
		if(c == null){
			try{
				c = convertCourseDTO(ccdto.getCourse(), s, b, u);
			}
			catch(Exception e){
				c = null;
			}
		}
		
		if(u == null){
			try{
				u = convertUserDTO(ccdto.getUser(), s, c, b);
			}
			catch(Exception e){
				u = null;
			}
		}
			
		
		try{
			return new CourseComment(new CourseCommentId(ccdto.getId()), c, u, ccdto.getComments(), ccdto.isFlagged());
		}
		catch(ValidationException e){
			throw new RuntimeException(e);
		}
	}
	
	private BookComment convertTextbookCommentDTO(TextbookCommentDTO tcdto, Section s, Course c, Book b, User u){
		if(b == null){
			try{
				b = convertTextbookDTO(tcdto.getTextbook(), s, c, u);
			}
			catch(Exception e){
				b = null;
			}
		}
		
		if(u == null){
			try{
				u = convertUserDTO(tcdto.getUser(), s, c, b);
			}
			catch(Exception e){
				u = null;
			}
		}
		
		try{
			return new BookComment(new BookCommentId(tcdto.getId()), b, u, tcdto.getComments(), tcdto.isFlagged());
		}
		catch(ValidationException e){
			throw new RuntimeException(e);
		}
	}

	private Set<TextbookCommentDTO> convertBookCommentsToDTOs(Collection<BookComment> bcs, TextbookDTO t, UserDTO u){
		if(bcs == null)
			return new HashSet<TextbookCommentDTO>();
		
		Set<TextbookCommentDTO> b = new HashSet<TextbookCommentDTO>(bcs.size());
		for(Iterator<BookComment> it = bcs.iterator(); it.hasNext();){
			b.add(convertBookCommentToDTO(it.next(), t, u));
		}
		return b;
	}
	
	private TextbookCommentDTO convertBookCommentToDTO(BookComment bc, TextbookDTO t, UserDTO u){
		if(bc == null)
			return null;
		
		if(t == null){
			try{
				t = convertBookToDTO(bc.getParentBook());
			}
			catch(Exception e){
				t = null;
			}
		}
		
		if(u == null){
			try{
				u = convertUserToDTO(bc.getUser());
			}
			catch(Exception e){
				u = null;
			}
		}
		
		TextbookCommentDTO tcdto = new TextbookCommentDTO();
		tcdto.setComments(bc.getComment());
		tcdto.setFlagged(bc.isFlagged());
		tcdto.setTextbook(t);
		tcdto.setUser(u);
		BookCommentId bcid = bc.getBookCommentId();
		if(bcid != null){
			tcdto.setId(bcid.getBookCommentIdLong().intValue());
		}
		else{
			Serializable s = HibernateHelper.getSession().save(tcdto);
			try{
				bc.setBookCommentId(new BookCommentId((Long)s));
			}
			catch(ValidationException e){
				e.printStackTrace();
			}
		}
		return tcdto;
	}
	
	private Set<CourseCommentDTO> convertCourseCommentsToDTOs(Collection<CourseComment> ccs){
		if(ccs == null)
			return new HashSet<CourseCommentDTO>();
		
		Set<CourseCommentDTO> c = new HashSet<CourseCommentDTO>();
		for(Iterator<CourseComment> it = ccs.iterator(); it.hasNext();){
			c.add(convertCourseCommentToDTO(it.next()));
		}
		return c;
	}

	private CourseCommentDTO convertCourseCommentToDTO(CourseComment cc){
		if(cc == null)
			return null;
		
		// IMPLEMENT DBDAOImpl.convertCourseCommentToDTO()
		return null;
	}

	private Set<SourceDTO> convertSourcesToDTOs(Collection<Source> srcs){
		if(srcs == null)
			return new HashSet<SourceDTO>();
		
		Set<SourceDTO> s = new HashSet<SourceDTO>(srcs.size());
		for(Iterator<Source> it = srcs.iterator(); it.hasNext();){
			s.add(convertSourceToDTO(it.next()));
		}
		return s;
	}
	
	private SourceDTO convertSourceToDTO(Source src){
		if(src == null)
			return null;
		
		// IMPLEMENT DBDAOImpl.convertSourceToDTO()
		return null;
	}
	
	private Set<SectionDTO> convertSectionsToDTOs(Collection<Section> sections){
		if(sections == null)
			return null;
		
		Set<SectionDTO> s = new HashSet<SectionDTO>(sections.size());
		for(Iterator<Section> it = sections.iterator(); it.hasNext();){
			s.add(convertSectionToDTO(it.next()));
		}
		return s;
	}

	private SectionDTO convertSectionToDTO(Section section){
		if(section == null)
			return null;
		
		// IMPLEMENT DBDAOImpl.convertSectionToDTO()
		return null;
	}

	private Set<Section> convertSectionDTOs(Collection<SectionDTO> sdtos, Section s, Course c, Book b, User u){
		if(sdtos == null)
			return null;
		
		Set<Section> ss = new HashSet<Section>(sdtos.size());
		for(Iterator<SectionDTO> it = sdtos.iterator(); it.hasNext();){
			SectionDTO sdto = it.next();
			try{
				if(s != null && s.getSectionId().equals(new SectionId((short)sdto.getId(), convertCourseDTO(sdto.getCourse(), s, b, u))))
					ss.add(s);
				else
					ss.add(convertSectionDTO(sdto, s, c, b, u));
			}
			catch(ValidationException e){
				throw new RuntimeException(e);
			}
		}
			
		return ss;
	}

	private Section convertSectionDTO(SectionDTO sdto, Section s, Course c, Book b, User u){
		if(sdto == null)
			return null;
		
		if(c == null){
			try{
				c = convertCourseDTO(sdto.getCourse(), s, b, u);
			}
			catch(Exception e){
				c = null;
			}
		}
		
		try{
			return new Section(new SectionId((short)sdto.getSectionNumber(), c), sdto.getProfessor(), convertTextbookDTOs(sdto.getTextbooks(), s, c, b, u));
		}
		catch(ValidationException e){
			throw new RuntimeException(e);
		}
	}
	
	private Set<User> convertUserDTOs(Collection<UserDTO> udtos, Section s, Course c, Book b, User u){
		if(udtos == null)
			return new HashSet<User>();
		
		Set<User> us = new HashSet<User>(udtos.size());
		for(Iterator<UserDTO> it = udtos.iterator(); it.hasNext();){
			UserDTO udto = it.next();
			try{
				if(u != null && u.getUsername().equals(new Username(udto.getUsername())))
					us.add(u);
				else
					us.add(convertUserDTO(udto, s, c, b));
			}
			catch(ValidationException e){
				throw new RuntimeException(e);
			}
		}
		return us;
	}
	
	private User convertUserDTO(UserDTO udto, Section s, Course c, Book b){
		if(udto == null)
			return null;
		
		try{
			User u = new User(
							new Username(udto.getUsername()),
							udto.getFirstName(),
							udto.getLastName(),
							udto.getPwd(),
							convertEmail(udto.getEmail()),
							udto.getLocation(),
							null,
							null,
							udto.getRole() == UserDTO.ROLE_ADMIN_USER,
							udto.isSuspended()
						);
			
			u.getMySections().addAll(convertSectionDTOs(udto.getSections(), s, c, b, u));
			u.getMyBooks().addAll(convertTextbookDTOs(udto.getTextbooks(), s, c, b, u));
			return u;
		}
		catch(ValidationException e){
			throw new RuntimeException(e);
		}
	}

	private EmailAddress convertEmail(String email) throws ValidationException{
		if(email == null)
			return null;
		
		return new EmailAddress(email);
	}
	
	private Set<Course> convertCourseDTOs(Collection<CourseDTO> cdtos, Section s, Book b, User u){
		if(cdtos == null)
			return null;
		
		Set<Course> cs = new HashSet<Course>(cdtos.size());
		for(Iterator<CourseDTO> it = cdtos.iterator(); it.hasNext();){
			cs.add(convertCourseDTO(it.next(), s, b, u));
		}
		return cs;
	}
	
	private Course convertCourseDTO(CourseDTO cdto, Section s, Book b, User u){
		if(cdto == null)
			return null;
		
		try{
			Course c = new Course(
							new CourseId(cdto.getCourseCode() + cdto.getCourseNumber()),
							cdto.getCourseTitle(),
							null,
							null
					);
			
			c.getSections().addAll(convertSectionDTOs(cdto.getSections(), s, c, b, u));
			c.getComments().addAll(convertCourseCommentDTOs(cdto.getCourseComments(), s, c, b, u));
			
			return c;
		}
		catch(ValidationException e){
			throw new RuntimeException(e);
		}
	}
	
	private Object checkForNullAttr(Object attr, String attrName) throws AttributeCannotBeNullException{
		if(attr == null)
			throw new AttributeCannotBeNullException(attrName);
		else
			return attr;
	}
}
